package cn.zhxu.toys.concurrent;

import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Redis并发同步锁
 * 
 * @author Troy.Zhou
 * 
 * @since 0.4.9
 */
public abstract class AbstractSyncLock implements SyncLock {


	static Logger log = LoggerFactory.getLogger(AbstractSyncLock.class);
	
	/**
	 * 缓存键值前缀
	 */
	private String keyPrefix = "lock";
	
	/**
	 * 服务节点ID
	 */
	private String nodeId;
	
	/**
	 * 锁最大持有时间
	 */
	private int maxHoldSeconds = 15;
	
	/**
	 * 锁最大等待时间
	 */
	private int maxWaitSeconds = 30;
	
	/**
	 * 重试间隔（单位毫秒）
	 */
	private int retryInterval = 50;
	
	@Override
	public void lock(String name) {
		lock(name, getRequestId(), maxHoldSeconds, maxWaitSeconds);
	}

	@Override
	public void lock(String name, int maxHoldSeconds) {
		lock(name, getRequestId(), maxHoldSeconds, maxWaitSeconds);
	}

	@Override
	public void lock(String name, int maxHoldSeconds, int maxWaitSeconds) {
		lock(name, getRequestId(), maxHoldSeconds, maxWaitSeconds);
	}
	
	@Override
	public void lock(String name, String requestId) {
		lock(name, requestId, maxHoldSeconds, maxWaitSeconds);
	}

	@Override
	public void lock(String name, String requestId, int maxHoldSeconds) {
		lock(name, requestId, maxHoldSeconds, maxWaitSeconds);
	}
	
	@Override
	public void lock(String name, String requestId, int maxHoldSeconds, int maxWaitSeconds) {
		String key = getLockKey(name);
		long ts = System.nanoTime() / 1000000;
		while (true) {
			if (doTryLock(key, requestId, maxHoldSeconds)) {
				return;
			}
			long tn = System.nanoTime() / 1000000;
			if (tn - ts > maxWaitSeconds * 1000L) {
				throw new LockException("获取锁等待超时！【name：" + name + "requestId：" + requestId + "】最大允许：" 
						+ maxWaitSeconds + "秒，已用时间：" + ((tn - ts) / 1000) + "秒！");
			}
			if (retryInterval <= 0) {
				continue;
			}
			try {
				Thread.sleep(retryInterval);
			} catch (InterruptedException e) {
				throw new LockException("Interrupted", e);
			}
		}
	}

	/**
	 * 上锁
	 * @param key 业务建
	 * @param requestId 请求标识
	 * @param maxHoldSeconds 最大持有时间
	 * @return 是否上锁成功
	 */
	protected abstract boolean doTryLock(String key, String requestId, int maxHoldSeconds);

	@Override
	public void release(String name) {
		release(name, getRequestId());
	}
	
	@Override
	public void release(String name, String requestId) {
		doUnLock(getLockKey(name), requestId);
	}

	/**
	 * 解锁
	 * @param key 业务建
	 * @param requestId 请求标识
	 */
	protected abstract void doUnLock(String key, String requestId);
	
	@Override
	public void with(String name, Runnable run) {
		with(name, getRequestId(), maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public void with(String name, int maxHoldSeconds, Runnable run) {
		with(name, getRequestId(), maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public void with(String name, int maxHoldSeconds, int maxWaitSeconds, Runnable run) {
		with(name, getRequestId(), maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public void with(String name, String requestId, Runnable run) {
		with(name, requestId, maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public void with(String name, String requestId, int maxHoldSeconds, Runnable run) {
		with(name, requestId, maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public void with(String name, String requestId, int maxHoldSeconds, int maxWaitSeconds, Runnable run) {
		lock(name, requestId, maxHoldSeconds, maxWaitSeconds);
		try {
			run.run();
		} finally {
			release(name, requestId);
		}
	}

	@Override
	public <V> V run(String name, Callable<V> run) {
		return run(name, getRequestId(), maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public <V> V run(String name, int maxHoldSeconds, Callable<V> run) {
		return run(name, getRequestId(), maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public <V> V run(String name, int maxHoldSeconds, int maxWaitSeconds, Callable<V> run) {
		return run(name, getRequestId(), maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public <V> V run(String name, String requestId, Callable<V> run) {
		return run(name, requestId, maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public <V> V run(String name, String requestId, int maxHoldSeconds, Callable<V> run) {
		return run(name, requestId, maxHoldSeconds, maxWaitSeconds, run);
	}

	@Override
	public <V> V run(String name, String requestId, int maxHoldSeconds, int maxWaitSeconds, Callable<V> run) {
		lock(name, requestId, maxHoldSeconds, maxWaitSeconds);
		try {
			return run.call();
		} catch (Exception e) {
			throw new LockException("锁内业务处理异常", e);
		} finally {
			release(name, requestId);
		}
	}

	protected String getLockKey(String name) {
		return keyPrefix + ":" + name;
	}
	
	protected String getRequestId() {
		long threadId = Thread.currentThread().getId();
		return nodeId + ":" + threadId;
	}

	public String getKeyPrefix() {
		return keyPrefix;
	}

	public void setKeyPrefix(String keyPrefix) {
		this.keyPrefix = keyPrefix;
	}

	public String getNodeId() {
		return nodeId;
	}

	public void setNodeId(String nodeId) {
		this.nodeId = nodeId;
	}

	public int getMaxHoldSeconds() {
		return maxHoldSeconds;
	}

	public void setMaxHoldSeconds(int maxHoldSeconds) {
		this.maxHoldSeconds = maxHoldSeconds;
	}

	public int getMaxWaitSeconds() {
		return maxWaitSeconds;
	}

	public void setMaxWaitSeconds(int maxWaitSeconds) {
		this.maxWaitSeconds = maxWaitSeconds;
	}

	public int getRetryInterval() {
		return retryInterval;
	}

	public void setRetryInterval(int retryInterval) {
		this.retryInterval = retryInterval;
	}
	
}
